﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmsQueueLib
{
    public class SmsQueueProcessor : BaseNonTransactionManager, IBackgroundTaskQueueProcessor
    {
        private BackgroundTaskResult taskResult; 
        private StringBuilder logBuilder;
        private TaskProgress onProgress;
        private Dictionary<Guid, ISmsSender> senderCache = new Dictionary<Guid, ISmsSender>();

        public BackgroundTaskResult ProcessTask(BackgroundTaskRequest request, TaskProgress i_onProgress, TaskInteractiveMessage i_onInteractiveMessage)
        {
            //make taskResult
            taskResult = new BackgroundTaskResult(request);
            //store progress delegate
            onProgress = i_onProgress;
            //new log
            logBuilder = new StringBuilder();

            if (request.taskInData is SmsSendCommand)
            {
                SmsSendCommand command = request.taskInData as SmsSendCommand;
                try
                {
                    int successCount = 0;
                    int failedCount = 0;

                    foreach (Guid recipient in command.Recipients)
                    {
                        //try to send each user
                        //if any fail mark task as failed
                        IUserRepository userRepository = ComponentFactory.GetTypedComponent<IUserRepository>("UserSecurity");

                        IContactPointRepository contactPointRepository = ComponentFactory.GetTypedComponent<IContactPointRepository>("Registers.ContactPointRepository");

                        bool sendOK = DoRecipientSend(userRepository, contactPointRepository, command, recipient);
                        if (sendOK)
                        {
                            successCount++;
                        }
                        else
                        {
                            failedCount++;
                        }
                    }

                    taskResult.taskLog = logBuilder.ToString();

                    taskResult.success = (successCount > 0 && failedCount == 0);

                    if (request.sendMessageOnSuccess)
                    {
                        NotifyRequestor(request, command, taskResult);
                    }
                }
                catch (Exception e)
                {
                    taskResult.success = false;
                    logBuilder.Append("Error message: " + e.Message);
                    taskResult.taskLog = logBuilder.ToString();
                    if (request.sendMessageOnFailure)
                    {
                        NotifyRequestor(request, command, taskResult);
                    }
                }
            }
            else
            {
                taskResult.taskLog = "Task input data not a sms send command";
            }
            return taskResult;
        }

        private bool DoRecipientSend(IUserRepository userRepository, IContactPointRepository contactPointRepository, SmsSendCommand command, Guid recipient)
        {
            bool result = false;

            string recipientDisplayName = string.Format("Unknown User [{0}]", recipient);

            try
            {
                //try to get proper name for recipient
                recipientDisplayName = userRepository.GetUserDisplayNameById(recipient);
                Guid userCentre = userRepository.GetUserCentre(recipient, false);
                if (userCentre != Guid.Empty)
                {
                    ISecurityManager securityManagerRepository = ComponentFactory.GetTypedComponent<ISecurityManager>("SecurityManager");
                    if (securityManagerRepository.CanSendSMS(userCentre))
                    {
                        ISmsSender sender = GetSmsSender(userCentre);
                        //get mobile phone data for recipient
                        ContactPointEntity smsContact = contactPointRepository.GetSmsNumberForEntity(recipient);
                        if (smsContact != null)
                        {
                            string number = smsContact.ContactPointText;
                            sender.SendMessage(number, command.MessageText);
                            result = true;
                            OnCommandLog(string.Format("Recipient: {0}, message sent.", recipientDisplayName));
                        }
                        else
                        {
                            OnCommandLog(string.Format("Recipient: {0}, no allowed mobile number.",
                            recipientDisplayName));
                        }
                    } //if centre has feature
                    else
                    {
                        OnCommandLog(string.Format("Recipient: {0}, does not have the SMS Notification feature.",
                        recipientDisplayName));
                    }
                }//if user has centre
                else
                {
                    OnCommandLog(string.Format("Recipient: {0}, has no centre.", recipientDisplayName));
                }
            }
            catch (Exception e)
            {
                OnCommandLog(string.Format("Recipient: {0}, error: {1}", recipientDisplayName, e.Message));
            }
            return result;
        }       

        private ISmsSender GetSmsSender(Guid centreId)
        {
            if (!senderCache.ContainsKey(centreId))
            {
                ICentreServiceSettingRepository centreServiceSettingsRepository =
                ComponentFactory.GetTypedComponent<ICentreServiceSettingRepository>("Registers.CentreServiceSettingRepository");
                Dictionary<string, string> smsSettings = centreServiceSettingsRepository.GetCentreServiceSettings(centreId).
                    Where(r => r.Key == "Sms Notify").ToDictionary(r => r.Key, r => r.Value);

                DesignByContract.ConditionCheck(smsSettings.Count > 0, "SMS Settings for Centre not found");
                Dictionary<string, object> config = new Dictionary<string, object>();
                config.Add("EndPointUri", "http://api.smsprovider.com/http/");
                config.Add("ApiId", smsSettings["ApiId"]);
                config.Add("User", smsSettings["Username"]);
                config.Add("Password", smsSettings["Password"]);
                //UK default country
                config.Add("CountryCode", 44);

                ISmsSender smsSender = new SmsProviderAdapter();
                smsSender.Initialise(config);
                smsSender.Login();
                senderCache.Add(centreId, smsSender);
            }
            return senderCache[centreId];
        }

        public void ExpireTask(Guid taskId)
        {
            //do nothing
        }

        private void NotifyRequestor(BackgroundTaskRequest request, SmsSendCommand command, BackgroundTaskResult result)
        {
            try
            {
                //send a message
                IMessageRepository messageRepository = (IMessageRepository)ComponentFactory.GetComponent("Registers.MessageRepository");

                string subject = string.Format("Sms Notify {0}", result.success ? "Complete" : "Failed");
                StringBuilder messageBuild = new StringBuilder();
                if (result.success)
                {
                    messageBuild.Append(String.Format("<p>The notification completed successfully.</p>"));
                }
                else
                {
                    messageBuild.Append(String.Format("<p>The notification failed.</p>"));
                }

                messageBuild.Append("<hr/>");
                messageBuild.Append(String.Format("<p>Notification message: {0}</p>", HtmlUtils.HtmlEncode(command.MessageText)));
                messageBuild.Append("<p>Notification log:</p>");
                messageBuild.Append("<p>");
                messageBuild.Append(HtmlUtils.FormatMultiLinePlainText(result.taskLog));
                messageBuild.Append("</p>");
                string message = messageBuild.ToString();
                messageRepository.SendSystemMessageToUser(request.requestingUserId, subject, message);
            }
            catch (Exception e)
            {
                //Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManager.Publish(e);
            }
        }

        public bool OnCommandProgress(decimal percentComplete)
        {
            if (onProgress != null)
            {
                return onProgress(taskResult.taskId, percentComplete);
            }
            else
            {
                return true;
            }
        }

        public void OnCommandLog(string message)
        {
            logBuilder.Append(message);
            logBuilder.Append("\r\n");
        }
    }

    public interface IBackgroundTaskQueueProcessor
    {
        BackgroundTaskResult ProcessTask(BackgroundTaskRequest request, TaskProgress OnProgress, TaskInteractiveMessage OnInteractiveMessage);
        void ExpireTask(Guid taskId);
    }

    /// <summary>
    /// delegate to allow task processors to inform manager of progress
    /// </summary>
    public delegate bool TaskProgress(Guid taskId, decimal percentComplete);
    /// <summary>
    /// delegate to allow task processors to inform interactive users about the task
    /// </summary>
    public delegate void TaskInteractiveMessage(String message);
}
